package com.example.springboot3demo.aspectJ;

import com.example.springboot3demo.annotation.MethodSync;
import com.example.springboot3demo.config.RabbitMQConfig;
import com.example.springboot3demo.mq.MessageProducer;
import com.example.springboot3demo.mq.dto.MethodInvocationDto;
import com.example.springboot3demo.util.MqProcessingMarker;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 方法同步切面
 * 拦截标记了@MethodSync注解的方法，将方法调用信息发送到RabbitMQ实现多实例同步
 *
 * @author Your Name
 * @date 2024-01-01
 */
@Slf4j
@Aspect
@Component
public class MethodSyncAspect {
    
    @Autowired
    private MessageProducer messageProducer;
    
    /**
     * 环绕通知，拦截标记了@MethodSync注解的方法
     * 
     * @param joinPoint 连接点
     * @param methodSync 方法同步注解
     * @return 方法执行结果
     * @throws Throwable 方法执行异常
     */
    @Around("@annotation(methodSync)")
    public Object syncMethod(ProceedingJoinPoint joinPoint, MethodSync methodSync) throws Throwable {
        // 检查当前线程是否正在处理MQ消息，如果是则跳过切面逻辑
        if (MqProcessingMarker.isProcessing()) {
            return joinPoint.proceed();
        }
        
        // 执行原方法
        Object result = joinPoint.proceed();
        
        try {
            // 构造方法调用信息
            MethodInvocationDto methodInvocationDto = buildMethodInvocationDto(joinPoint, methodSync);
            
            // 发送到RabbitMQ
            messageProducer.sendObjectMessage(RabbitMQConfig.METHOD_SYNC_EXCHANGE, methodSync.group(), methodInvocationDto);
            
            log.info("方法调用信息已发送到RabbitMQ: {}", methodInvocationDto);
        } catch (Exception e) {
            log.error("发送方法调用信息到RabbitMQ失败", e);
        }
        
        return result;
    }
    
    /**
     * 构造方法调用信息DTO
     * 
     * @param joinPoint 连接点
     * @param methodSync 方法同步注解
     * @return 方法调用信息DTO
     */
    private MethodInvocationDto buildMethodInvocationDto(ProceedingJoinPoint joinPoint, MethodSync methodSync) {
        // 获取方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        
        // 获取类名、方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        
        // 获取参数类型
        Class<?>[] parameterTypes = signature.getParameterTypes();
        String[] parameterTypeNames = new String[parameterTypes.length];
        for (int i = 0; i < parameterTypes.length; i++) {
            parameterTypeNames[i] = parameterTypes[i].getName();
        }
        
        // 获取参数值
        Object[] parameterValues = joinPoint.getArgs();
        
        // 构造DTO
        return new MethodInvocationDto(
                className,
                methodName,
                parameterTypeNames,
                parameterValues,
                System.currentTimeMillis(),
                methodSync.group()
        );
    }
}